 /* Copyright (C)
  * 2024 ZKLX Optoelectronic Technology Co.LTD
  * All rights reserved.
  */
 /**
  * @file cam_ctrl.cpp
  * @brief 海康威视工业面阵相机控制接口实现
  * @author xuke
  * @version 1.0
  * @date 2024-06-07
  */

#include "/home/xuke/bin/dbg.h"
#include <iostream>
#include <unistd.h>
#include "lx_serial.h"
#include "cam_hik_ctrl.h"
#include "lx_elog/finsh.h"
#define LOG_TAG cam_hik_ctrl
#include "lx_elog/stor_elog.h"

/*--------------------------------extern--------------------------------------------*/

/*--------------------------------globals-------------------------------------------*/

/*--------------------------------locals--------------------------------------------*/
static serial *camera_serial = nullptr;
static bool is_serial_opened = false;

/*--------------------------------funcs---------------------------------------------*/
/**
 * @brief 相机控制命令执行
 * @cmd   命令字符串
 * @return 0-success
 */
static int cam_ctrl_cmd_exec(uint8_t *cmd, uint32_t cmd_len, uint8_t *ret, uint32_t ret_size)
{
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};

	if (!cmd || !ret) {
		log_e("input args error");
		return -1;
	}

	snprintf((char*)tx_buf, cmd_len+2, "%s\n", cmd);
	camera_serial->duplexPairs(tx_buf, ret, strlen((char*)tx_buf), ret_size);
	//printf("ret:[%s]\n", ret);
	return 0;
}

int cam_hik_write_key_val(std::string key, std::string val)
{
	if (!is_serial_opened) {
		return -1;
	}
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	if (!key.length() || !val.length()) {
		log_d("key or val is NULL");
		return -1;
	}

	snprintf((char*)tx_buf, sizeof(tx_buf), "w %s %s", key.c_str(), val.c_str());
	printf("%s\n", tx_buf);

	return cam_ctrl_cmd_exec(tx_buf, strlen((char*)tx_buf), rx_buf, sizeof(rx_buf));
}

int cam_hik_read_key_val(std::string key, std::string &val)
{
	if (!is_serial_opened) {
		return -1;
	}
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};
	char *ptr = NULL;

	if (!key.length()) {
		printf("key is NULL\n");
		return -1;
	}

	snprintf((char*)tx_buf, sizeof(tx_buf), "r %s", key.c_str());

	if (!cam_ctrl_cmd_exec(tx_buf, strlen((char*)tx_buf), rx_buf, sizeof(rx_buf))) {
		if (!strstr((char*)rx_buf, "Success")) {
			return -1;
		}

		ptr = strrchr((char*)rx_buf, ':');
		val = ptr+1;
	}
	return 0;
}

/**
 * @brief 获取相机曝光模式
 * @return exposure_mode
 */
int16_t cam_ctrl_get_exposure_mode()
{
	uint8_t cmd_str[] = "r ExposureAuto";
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};
	uint16_t exposure_mode = 0;
	char *ptr = NULL;

	if (!cam_ctrl_cmd_exec(cmd_str, sizeof(cmd_str), rx_buf, sizeof(rx_buf))) {
		if (!strstr((char*)rx_buf, "Success")) {
			return -1;
		}

		ptr = strstr((char*)rx_buf, "ExposureAuto:");
		if (ptr) {
			sscanf(ptr, "ExposureAuto:%d", (int*)&exposure_mode); //0-off
		}
	}

	return exposure_mode;
}

/**
 * @brief 获取相机曝光时间
 * @return exposure_val
 */
int16_t cam_ctrl_get_exposure_time()
{
	uint8_t cmd_str[] = "r ExposureTime";
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};
	uint16_t exposure_val = 0;
	char *ptr = NULL;

	if (!cam_ctrl_cmd_exec(cmd_str, sizeof(cmd_str), rx_buf, sizeof(rx_buf))) {
		if (!strstr((char*)rx_buf, "Success")) {
			return -1;
		}

		ptr = strstr((char*)rx_buf, "ExposureTime:");
		if (ptr) {
			sscanf(ptr, "ExposureTime:%d", (int*)&exposure_val);
		}
	}

	return exposure_val;
}

/**
 * @brief 获取相机增益值
 * @return gain_val
 */
int16_t cam_ctrl_get_gain_val()
{
	uint8_t cmd_str[] = "r GainAbsVal";
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};
	uint16_t gain_val = 0;
	char *ptr = NULL;

	if (!cam_ctrl_cmd_exec(cmd_str, sizeof(cmd_str), rx_buf, sizeof(rx_buf))) {
		if (!strstr((char*)rx_buf, "Success")) {
			return -1;
		}

		ptr = strstr((char*)rx_buf, "GainAbsVal:");
		if (ptr) {
			sscanf(ptr, "GainAbsVal:%d", (int*)&gain_val);
		}
	}

	return gain_val;
}

/**
 * @brief 相机曝光模式设置
 * @param mode - 0x00手动曝光, 0x0F自动曝光
 * @return 0-success
 */
int cam_ctrl_set_exposure_mode(uint8_t mode)
{
	log_d("set exposure mode to 0x%02x", mode);
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	if (mode == 0x00) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w ExposureAuto 0\n");
	} else if (mode == 0x0F) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w ExposureAuto 2\n");
	} else {
		log_e("exposure mode must be [0x00, 0x0F]");
		return -1;
	}
	camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf));
	log_d("rx_buf:[%7s]", rx_buf);

	if (!strstr((char*)rx_buf, "Success")) {
		//log_e("exposure mode set failed!");
		return -2;
	}
	return 0;
}

/**
 * @brief 相机增益模式设置
 * @param mode - 0x00手动增益, 0x0F自动增益
 * @return 0-success
 */
int cam_ctrl_set_gain_mode(uint8_t mode)
{
	log_d("set gain mode to 0x%02x", mode);
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	if (mode == 0x00) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w GainCtrl 0\n");
	} else if (mode == 0x0F) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w GainCtrl 1\n");
	} else {
		log_e("gain mode must be [0x00, 0x0F]");
		return -1;
	}
	camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf));
	log_d("rx_buf:[%s]", rx_buf);

	if (!strstr((char*)rx_buf, "Success")) {
		//log_e("exposure gain_mode set failed!");
		return -2;
	}
	return 0;
}

/**
 * @brief 相机曝光时间设置
 * @param time - 曝光时间, 24~65535, 单位usec
 * @return 0-success
 */
int cam_ctrl_set_exposure_time(uint16_t time)
{
	log_d("set exposure time to %d usec", time);
	if (time < 24) {
		log_e("exposure time range must in [24~65535]");
		return -1;
	}

	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	snprintf((char*)tx_buf, sizeof(tx_buf), "w ExposureTime %d\n", time);
	camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf));
	log_d("rx_buf:[%s]", rx_buf);

	if (!strstr((char*)rx_buf, "Success")) {
		log_e("exposure exposure time set failed!");
		return -2;
	}
	return 0;
}

/**
 * @brief 相机增益值设置
 * @param gain_val - 增益值, 33~512
 * @return 0-success
 */
int cam_ctrl_set_gain_val(uint16_t gain_val)
{
	log_d("set gain value time to %d", gain_val);
	if (gain_val < 64 || gain_val > 622) {
		log_e("gain value range must in [64~622]");
		return -1;
	}

	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	snprintf((char*)tx_buf, sizeof(tx_buf), "w GainAbsVal %d\n", gain_val);
	camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf));
	log_d("rx_buf:[%s]", rx_buf);

	if (!strstr((char*)rx_buf, "Success")) {
		log_e("exposure gain_val set failed!");
		return -2;
	}
	return 0;
}

/**
 * @brief 相机触发模式设置
 * @param mode - 0x00内触发, 0xF外触发
 * @return 0-success
 */
int cam_ctrl_set_trigger_mode(uint8_t mode)
{
	log_d("set trigger mdoe to 0x%02x", mode);
	uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
	uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};

	if (mode == 0x00) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w TriggerMode 0\n");
	} else if (mode == 0x0F) {
		snprintf((char*)tx_buf, sizeof(tx_buf), "w TriggerMode 64\n");
	} else {
		log_e("trigger mode must be [0x00, 0x0F]");
		return -1;
	}
	camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf));
	log_d("rx_buf:[%s]", rx_buf);

	if (!strstr((char*)rx_buf, "Success")) {
		log_e("exposure trigger mode set failed!");
		return -2;
	}
	return 0;
}

/**
 * @brief 相机控制接口初始化
 * @return 0-success
 */
int cam_hik_dev_init(int speed)
{
	camera_serial = new serial("/dev/ttyTHS1", speed, 8, 1);
	//camera_serial = new serial("/dev/ttyS17", speed, 8, 1);

	if (camera_serial->open()) {
		log_e("camera_serial open failed!");
		return -1;
	}

	is_serial_opened = true;
	log_i("camera_serial open success");
	return 0;
}

int cam_hik_dev_close()
{
	is_serial_opened = false;
	camera_serial->close();
	return 0;
}

void show_hik_cmd_list()
{
	int i = 0;
	printf("%02d. w DeviceReset 1\n",  ++i);
	printf("%02d. w Width 1920\n",     ++i);
	printf("%02d. w Height 1080\n",    ++i);
	printf("%02d. w OffsetX 256\n",    ++i);
	printf("%02d. w OffsetY 484\n",    ++i);
	printf("%02d. w ReverseX <0|1>\n", ++i);
	printf("%02d. w ReverseY <0|1>\n", ++i);
	printf("%02d. w PixelFormat <0x01080001:Mono8, 0x01100003:Mono10, 0x01100005:Mono12, 0x02180014:RGB8, 0x01080009:BayerRG8, 0x0110000d:BayerRG10, 0x01100011:BayerRG12>\n", ++i);
	printf("%02d. w TriggerMode <0|64>\n", ++i);
	printf("%02d. w TriggerSource+6 <0:line0, 1:line1, 2:line2, 4:Counter0, 7:Software, 9:CC1, 11:CC2, 12:CC3, 13:CC4>\n", ++i);
	printf("%02d. w ExposureTime 2000\n",  ++i);
	printf("%02d. w ExposureAuto <0:off, 1:once, 2:continues>\n", ++i);
	printf("%02d. w GainAbsVal 64\n",      ++i);
	printf("%02d. w GainCtrl <0:off, 1:once, 2:continues>\n",     ++i);
	printf("%02d. w DeviceTapGeometry <0x01010101:G_1X_1Y, 0x01020101:G_1X2_1Y, 0x01020102:G_1X2_1Y2, 0x0a0b0c0d:G_aXb_cYd>\n", ++i);
	printf("%02d. r ClConfiguration\n", ++i);
	printf("%02d. w AcquisitionFrameRate 1000000\n", ++i);
	printf("%02d. w Brightness 127\n",  ++i);
	printf("%02d. w AutoExposureTimeUpperLimit 8500\n", ++i);
	printf("%02d. w AutoExposureTimeLowerLimit 1000\n", ++i);
	printf("%02d. w UserSetSave 1\n",   ++i);
}

/**
 * @brief 相机控制调试命令
 * @param argc
 * @param argv[]
 * @return 0 - success
 */
int shell_hik_camer_cmd(int argc, const char *argv[])
{
	if (argc < 2) {
		printf("Usage: %s <cmd|help> <value>\n", argv[0]);
		return -1;
	}

	if (!strcmp(argv[1], "cmd")) {
		uint8_t tx_buf[CAM_CMD_MAX_LEN] = {0};
		uint8_t rx_buf[CAM_CMD_MAX_LEN] = {0};
		snprintf((char*)tx_buf, sizeof(tx_buf), "%s\n", argv[2]);
		camera_serial->duplexPairs(tx_buf, rx_buf, strlen((char*)tx_buf), sizeof(rx_buf), 100);
		printf("rx_buf:[%s]\n", rx_buf);
	} else if (!strcmp(argv[1], "help")) {
		show_hik_cmd_list();
	} else {
		printf("Usage: %s <cmd|help> <value>\n", argv[0]);
		return -2;
	}
	return 0;
}
FINSH_FUNCTION_EXPORT(shell_hik_camer_cmd, send cmd to hik camera)

SPDK_LOG_REGISTER_COMPONENT(cam_hik_ctrl)
